package com.xb.loan.cashier.biz.task;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.xb.loan.cashier.biz.dao.bean.CashierAccount;
import com.xb.loan.cashier.biz.dao.bean.CashierAccountDetail;
import com.xb.loan.cashier.biz.dao.bean.CashierPayChannel;
import com.xb.loan.cashier.biz.service.CashierAccountService;
import com.xb.loan.cashier.biz.service.CashierChargeOrderService;
import com.xb.loan.cashier.biz.service.CashierEncashOrderService;
import com.xb.loan.cashier.biz.service.CashierPayChannelService;
import com.xb.loan.cashier.biz.task.bean.ThirdAccountBean;
import com.xb.loan.cashier.service.constant.ICashierEnumConst;
import com.xb.loan.log.exception.BizException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 与第三方提现对账任务
 * 该类处理第三方返回的对账文件包含商户(收银台)流水号，且只包含成功数据的，当收银台状态为处理中第三方为成功时以第三方为准
 *
 *
 */
public abstract class ThirdAccountTask extends BaseTask {
    private static final Logger logger = LoggerFactory.getLogger(ThirdAccountTask.class);
    protected static final int PAGE_SIZE = 500;
    
    protected String day;                               // 对账日期
    protected Date accountDay;
    protected String payChannelId;                      // 第三方渠道信息
    protected CashierPayChannel channel;
    
    protected boolean hasNextPage = true;               // 查询第三方时是否有下一页
    
    public void setDay(String day) {
        this.day = day;
    }
    public void setPayChannelId(String payChannelId) {
        this.payChannelId = payChannelId;
    }
    
    protected CashierAccountService cashierAccountService;
    
    protected BigDecimal thirdTotalMoney = new BigDecimal(0);             // 第三方成功总金额
    protected int thirdTotalCount = 0;                                    // 第三方成功总笔数
    
    protected BigDecimal cashierTotalMoney = new BigDecimal(0);           // 收银台成功总金额
    protected int cashierTotalCount = 0;                                  // 收银台成功总笔数
    
    protected BigDecimal thirdLessMoney = new BigDecimal(0);                // 第三方少的数据总金额
    protected int thirdLessCount = 0;                                       // 第三方少的数据总笔数
    protected List<String> thirdLessOrderIds = new ArrayList<String>();     // 第三方少的订单号
    
    protected BigDecimal cashierLessMoney = new BigDecimal(0);              // 收银台少的数据总金额
    protected int cashierLessCount = 0;                                     // 收银台少的数据总笔数
    protected List<String> cashierLessOrderIds = new ArrayList<String>();   // 收银台少的订单号
    
    protected BigDecimal equalMoney = new BigDecimal(0);                    // 对账成功的数据总金额
    protected int equalCount = 0;                                           // 对账成功的数据总笔数
    
    protected int noEqualCount = 0;                                         // 不一致的总笔数
    protected List<String> noEqualOrderIds = new ArrayList<String>();       // 不一致的订单号
    
    
    protected CashierPayChannelService cashierPayChannelService;
    protected CashierEncashOrderService cashierEncashOrderService;
    protected CashierChargeOrderService cashierChargeOrderService;
    
    @Override
    public void execute() {
        // 验证参数
        if(!validate()){
            return;
        }
        
        doExecute();
    }

    
    private boolean validate() {
        try {
            if(StringUtils.isBlank(payChannelId)|| StringUtils.isBlank(day)){
                logger.warn("msg=参数为空 payChannelId="+payChannelId+" day="+day);
                return false;
            }
            this.accountDay = new SimpleDateFormat("yyyyMMdd").parse(day);
            cashierPayChannelService = (CashierPayChannelService) ctx.getBean("cashierPayChannelService");
            this.channel = cashierPayChannelService.selectByPayChannelId(payChannelId);
            if(channel==null){
                logger.warn("msg=第三方渠道不存在 payChannelId="+payChannelId+" day="+day);
                return false;
            }
        } catch (ParseException e) {
            logger.error("msg=对账日期格式不对 day="+day+" payChannelId="+payChannelId);
            return false;
        }
        return true;
    }


    protected void doExecute(){
    	logger.info("ThirdAccountTask doExecute start");
        cashierEncashOrderService = (CashierEncashOrderService) ctx.getBean("cashierEncashOrderService");
        cashierAccountService = (CashierAccountService) ctx.getBean("cashierAccountService");
        cashierChargeOrderService = (CashierChargeOrderService)ctx.getBean("cashierChargeOrderService");
        
        // 1.处理历史数据、初始化对账总表
        cashierAccountService.dealHistoryAccountResult(getPayChannelType(), getOrderType(), day);
        String accountId = cashierAccountService.selectAccountId();
        cashierAccountService.insertAccount(accountId, getPayChannelType(), getOrderType(), day);
        
        // 2.根据第三方返回的数据，查询对应的数据库中数据进行对比
        accountThirdDatas(accountId);
        
        // 3.第三方账期内数据库成功，但第三方缺的数据（account_day为day，但是状态为未对账的）。可能是手动修改或者或者之前对账数据或者其他情况的数据
        accountThirdLessDatas(accountId);
        
        // 4.账期内未返回的成功数据。(成功且未对账，但account_period为day的)。可能是跨账期会在以后账期返回，或者是业务逻辑错误收银台成功但第三方失败的
        accountAccountNonDatas(accountId);
        
        // 更新对账总表数据
        int accountStatus = ICashierEnumConst.CashierAccountStatus.SUCCESS.getStatus();
        if(thirdLessCount>0||cashierLessCount>0||noEqualCount>0){
            accountStatus= ICashierEnumConst.CashierAccountStatus.FAIL.getStatus();
        }
        CashierAccount cashierAccount = new CashierAccount();
        cashierAccount.setAccountDay(day);
        cashierAccount.setCashierAccountId(accountId);
        cashierAccount.setOrderType(getOrderType());
        cashierAccount.setPaychannelType(getPayChannelType());
        cashierAccount.setStatus(accountStatus);
        cashierAccount.setCashierMoney(cashierTotalMoney.setScale(2, BigDecimal.ROUND_DOWN).doubleValue());
        cashierAccount.setThirdMoney(thirdTotalMoney.setScale(2, BigDecimal.ROUND_DOWN).doubleValue());
        cashierAccount.setCashierCount(cashierTotalCount);
        cashierAccount.setThirdCount(thirdTotalCount);
        cashierAccount.setCashierLessCount(cashierLessCount);
        cashierAccount.setThirdLessCount(thirdLessCount);
        cashierAccount.setEqualCount(equalCount);
        cashierAccount.setNoEqualCount(noEqualCount);
        cashierAccountService.updateAccount(cashierAccount);
    }
    
    private void accountAccountNonDatas(String accountId) {
        int pageNo = 1;
        while(true){
            List<ThirdAccountBean> noAccountDatas = getAccountNonDatas(day, accountId, pageNo++, PAGE_SIZE);
            if(CollectionUtils.isEmpty(noAccountDatas)){
                break;
            }
            cashierAccountService.insertAccountNon(getPayChannelType(), getOrderType());
            CashierAccount account = cashierAccountService.selectAccountNon(getPayChannelType(), getOrderType());
            for(ThirdAccountBean order : noAccountDatas){
                // 需要添加到账期明细数据，不属于任何一个账期
                addAccountDetail(order.getOrderId(), order.getOrderIdThird(), account.getCashierAccountId(), 
                        CashierAccountService.NOT_ACCOUNT_DAY, order.getAccountDay(), ICashierEnumConst.CashierAccountDetailStatus.THIRD_LESS_ERROR.getStatus(),
                        order.getMoney(), 0);
            }
        }
    }
    
    
    private void accountThirdLessDatas(String accountId) {
        int pageNo = 1;
        while(true){
            List<ThirdAccountBean> thirdLessDataList = getThirdLessDatas(day, accountId, pageNo++, PAGE_SIZE);
            if(thirdLessDataList==null||thirdLessDataList.size()<1){
                break;
            }
            for(ThirdAccountBean order : thirdLessDataList){
                thirdLessMoney = thirdLessMoney.add(new BigDecimal(order.getMoney()));
                thirdLessCount++;
                thirdLessOrderIds.add(order.getOrderId());  
                thirdLessMoney = cashierTotalMoney.add(new BigDecimal(order.getMoney()));
                cashierTotalCount++;
                addAccountDetail(order.getOrderId(), "", accountId, day, order.getAccountDay(), 
                        ICashierEnumConst.CashierAccountDetailStatus.THIRD_LESS_ERROR.getStatus(), order.getMoney(), 0);
            }
        }
    }
    
    
    private void addAccountDetail(String orderId, String channelOrderId,
                                  String accountId, String accountDayThird, String accountDayPlat,
                                  int accountStatus, double money, double thirdMoney) {
        CashierAccountDetail accountDetail = new CashierAccountDetail();
        accountDetail.setAccountDay(accountDayThird);
        accountDetail.setCashierAccountDetailId(cashierAccountService.selectAccountId());
        accountDetail.setChannelOrderId(channelOrderId);
        accountDetail.setMoney(money);
        accountDetail.setOrderId(orderId);
        accountDetail.setOrderType(getOrderType());
        accountDetail.setPaychannelType(getPayChannelType());
        accountDetail.setStatus(accountStatus);
        accountDetail.setThirdMoney(thirdMoney);
        accountDetail.setCashierAccountId(accountId);
        accountDetail.setAccountDayPlat(accountDayPlat);
        cashierAccountService.insertAccountDetail(accountDetail);
    }
    
    private void accountThirdDatas(String accountId) {
        Map<String, ThirdAccountBean> tempThirdDatas = null;
        List<String> tempIds = null;
        int pageNo = 1;
        while(true){
            tempIds = new ArrayList<String>();
            
            if(!hasNextPage){
                break;
            }
            tempThirdDatas = getThirdAccountDatas(pageNo++, PAGE_SIZE, day, channel);
            if(tempThirdDatas==null || tempThirdDatas.size()<1){
                break;
            }
            for(ThirdAccountBean thirdData : tempThirdDatas.values()){
                tempIds.add(thirdData.getOrderId());
                thirdTotalMoney = thirdTotalMoney.add(new BigDecimal(thirdData.getMoney()));
                thirdTotalCount++;
            }
            if(!hasThirdAccountTime()){// 没有第三方账期时间，需要更新第三方账期时间
                updateThirdAccountTimeBatch(tempIds);
            }
            
            // 对比数据
            List<ThirdAccountBean> tempDBDatasList = getDBAccountDatasByIds(tempIds);
            if(CollectionUtils.isNotEmpty(tempDBDatasList)){
                for(ThirdAccountBean dbData : tempDBDatasList){
                    try {
                        ThirdAccountBean thirdData = tempThirdDatas.get(dbData.getOrderId());
                        int accountStatus = compareAndUpdateDetail(dbData, thirdData, accountId);
                        if(dbData.getStatus()<=ThirdAccountBean.STATUS_SUCCESS){// 收银台成功的数据
                            cashierTotalMoney = cashierTotalMoney.add(new BigDecimal(dbData.getMoney()));
                            cashierTotalCount++;
                        }
                        tempIds.remove(dbData.getOrderId());
                        
                        // 统计
                        if(accountStatus== ICashierEnumConst.CashierAccountDetailStatus.SUCCESS.getStatus() && dbData!=null){
                            equalMoney = equalMoney.add(new BigDecimal(dbData.getMoney()));
                            equalCount++;
                        }else if(accountStatus== ICashierEnumConst.CashierAccountDetailStatus.DETAIL_ERROR.getStatus()){
                            noEqualOrderIds.add(dbData.getOrderId());
                            noEqualCount++;
                        }else if(accountStatus== ICashierEnumConst.CashierAccountDetailStatus.CASHIER_LESS_ERROR.getStatus()){
                            cashierLessMoney = cashierLessMoney.add(new BigDecimal(dbData.getMoney()));
                            cashierLessCount++;
                            cashierLessOrderIds.add(dbData.getOrderId());
                        }else if(accountStatus== ICashierEnumConst.CashierAccountDetailStatus.THIRD_LESS_ERROR.getStatus()){
                            thirdLessMoney = thirdLessMoney.add(new BigDecimal(dbData.getMoney()));
                            thirdLessCount++;
                            thirdLessOrderIds.add(dbData.getOrderId());
                        }
                    } catch (Exception e) {
                        logger.error("accountThirdDatas 处理异常", e);
                    }
                }
            }
            
            // 收银台少的数据
            for(String orderId : tempIds){
                ThirdAccountBean tempOrder = tempThirdDatas.get(orderId);
                cashierLessMoney = cashierLessMoney.add(new BigDecimal(tempOrder.getMoney()));
                cashierLessCount++;
                cashierLessOrderIds.add(tempOrder.getOrderId());
            }
        }
    }
    
    protected int compareAndUpdateDetail(ThirdAccountBean dbData, ThirdAccountBean thirdData, String accountId) throws BizException {
        int accountStatus = ICashierEnumConst.CashierAccountDetailStatus.SUCCESS.getStatus();
        String orderId = thirdData.getOrderId();
        
        if(dbData==null){
            accountStatus = ICashierEnumConst.CashierAccountDetailStatus.CASHIER_LESS_ERROR.getStatus();
        }else{
            accountStatus = compare(dbData, thirdData, true)? ICashierEnumConst.CashierAccountDetailStatus.SUCCESS.getStatus():
                ICashierEnumConst.CashierAccountDetailStatus.DETAIL_ERROR.getStatus();
            
            int tradeStatus = ThirdAccountBean.STATUS_SUCCESS;// 默认对账文件都是成功的
            // 如果只是状态不一致，且收银台未处理中，以第三方对账文件为准，需要修改数据库状态
            if(accountStatus!= ICashierEnumConst.CashierAccountDetailStatus.SUCCESS.getStatus() && dbData!=null && compare(dbData, thirdData, false)){
                if(dbData.getStatus()==ThirdAccountBean.STATUS_WAIT){
                    updateResult(dbData.getOrderId(), tradeStatus);
                    accountStatus = ICashierEnumConst.CashierAccountDetailStatus.SUCCESS.getStatus();
                }else if(dbData.getStatus()== ICashierEnumConst.CashierEncashStatus.FAIL.getStatus()){
                    logger.info("msg=状态不一致，对账文件为成功，数据库为失败"+" orderId="+dbData.getOrderId());
                    accountStatus = ICashierEnumConst.CashierAccountDetailStatus.DETAIL_ERROR.getStatus();
                }
            }
        }
        updateAccountStatus(accountId, orderId, accountStatus, thirdData.getAccountDayThird());
        
        if(accountStatus== ICashierEnumConst.CashierAccountDetailStatus.SUCCESS.getStatus()){
            // 删除非day账期，且已对账的数据
            if(!day.equals(dbData.getAccountDay())){
                cashierAccountService.deleteAccountDetail(dbData.getOrderId());
            }
        }else{
            String channelOrderId = thirdData!=null?thirdData.getOrderIdThird():(dbData==null?null:dbData.getOrderIdThird());
            double money = dbData==null?0:dbData.getMoney();
            double thirdMoney = thirdData==null?0:thirdData.getMoney();
            String accountDayPlat = thirdData!=null?thirdData.getAccountDay():(dbData==null?null:dbData.getAccountDay());
            addAccountDetail(orderId, channelOrderId, accountId, day, accountDayPlat, accountStatus, money, thirdMoney);
        }
        return accountStatus;
    }
    
    
    /**
     * 获取渠道类型，如支付宝
     * @return
     */
    protected abstract int getPayChannelType();
    /**
     * 获取订单类型，充值或提现
     * @return
     */
    protected abstract int getOrderType();

    /**
     * 分页查询第三方数据
     * @param pageNo
     * @param pageSize
     * @param day
     * @param channel
     * @return
     */
    protected abstract Map<String, ThirdAccountBean> getThirdAccountDatas(int pageNo, int pageSize,
                                                                          String day, CashierPayChannel channel);
    
    /**
     * 是否有第三方交易时间
     * @return
     */
    protected abstract boolean hasThirdAccountTime();
    
    /**
     * 批量更新订单的账期时间
     * hasThirdAccountTime()为true的方法需要实现
     * @param tempIds
     */
    protected abstract void updateThirdAccountTimeBatch(List<String> tempIds);
    
    protected abstract List<ThirdAccountBean> getDBAccountDatasByIds(List<String> orderIds);
    
    protected abstract List<ThirdAccountBean> getThirdLessDatas(String day, String accountId, int pageNo, int pageSize);
    
    protected abstract List<ThirdAccountBean> getAccountNonDatas(String day, String accountId, int pageNo, int pageSize);
    /**更新对账状态**/
    protected abstract boolean updateAccountStatus(String accountId, String orderId, int accountStatus, String day);
    /**对比明细**/
    protected abstract boolean compare(ThirdAccountBean dbData, ThirdAccountBean thirdData, boolean compareStatas);
    /**当收银台状态为处理中，对账文件为完成时，更新数据库数据状态**/
    protected abstract int updateResult(String orderId, int tradeStatus);
}
